package mcpplus

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/hongzhaomin/gspring/tools"
	"gitee.com/hongzhaomin/hzm-common-go/strutil"
	"gitee.com/hongzhaomin/hzm-common-go/toolkit"
	"github.com/invopop/jsonschema"
	"github.com/mark3labs/mcp-go/mcp"
	"reflect"
	"slices"
	"strconv"
	"strings"
)

const (
	jsonschemaTag = "jsonschema"
	name          = "name"
	description   = "description"
	required      = "required"
	enum          = "enum"
	title         = "title"

	toolDescTag   = "toolDesc" // 函数描述tag
	defaultValTag = "default"  // 默认值tag
)

var (
	mcpToolRt = reflect.TypeOf((*Tool)(nil)).Elem()
)

func NewToolParser() *ToolParser {
	return &ToolParser{
		funcName2Mapping: make(map[string]*FuncMapping, 16),
	}
}

type ToolParser struct {
	funcName2Mapping map[string]*FuncMapping
	mcpTools         []mcp.Tool
}

// buildMcpTools 构建mcp工具对象
func (my *ToolParser) buildMcpTools() []mcp.Tool {
	var mcpTools []mcp.Tool
	for funcName, funcMapping := range my.funcName2Mapping {
		var toolOpts []mcp.ToolOption
		toolOpts = append(toolOpts, mcp.WithDescription(funcMapping.description))

		funcParams := funcMapping.funcParams
		if len(funcParams) == 1 && funcParams[0].rtParam.Kind() == reflect.Struct && funcParams[0].schema != nil {
			schema := funcParams[0].schema
			requireds := schema.Required
			properties := schema.Properties
			for pair := properties.Oldest(); pair != nil; pair = pair.Next() {
				pName := pair.Key
				propertiesSchema := pair.Value

				propertyOpts := []mcp.PropertyOption{
					mcp.Description(propertiesSchema.Description),
				}

				if slices.Contains(requireds, pName) {
					propertyOpts = append(propertyOpts, mcp.Required())
				}

				var enums []string
				for _, e := range propertiesSchema.Enum {
					if eStr, err := toolkit.ConvertStr4Any(e); err == nil {
						enums = append(enums, eStr)
					}
				}
				if len(enums) > 0 {
					propertyOpts = append(propertyOpts, mcp.Enum(enums...))
				}

				switch propertiesSchema.Type {
				case typeStr:
					toolOpts = append(toolOpts, mcp.WithString(pName, propertyOpts...))
				case typeInt:
					toolOpts = append(toolOpts, mcp.WithNumber(pName, propertyOpts...))
				case typeNumber:
					toolOpts = append(toolOpts, mcp.WithNumber(pName, propertyOpts...))
				case typeBool:
					toolOpts = append(toolOpts, mcp.WithBoolean(pName, propertyOpts...))
				case typeObj:
					toolOpts = append(toolOpts, mcp.WithObject(pName, propertyOpts...))
				case typeArray:
					toolOpts = append(toolOpts, mcp.WithArray(pName, propertyOpts...))
				default:
					fmt.Println("未知类型：", propertiesSchema.Type)
				}
			}
		} else if len(funcParams) > 0 {
			for _, param := range funcParams {
				pName := param.pName

				propertyOpts := []mcp.PropertyOption{
					mcp.Description(param.desc),
				}

				if param.required {
					propertyOpts = append(propertyOpts, mcp.Required())
				}

				if param.defaultVal != "" {
					propertyOpts = append(propertyOpts, mcp.DefaultString(param.defaultVal))
				}

				if len(param.enum) > 0 {
					propertyOpts = append(propertyOpts, mcp.Enum(param.enum...))
				}

				if param.title != "" {
					propertyOpts = append(propertyOpts, mcp.Title(param.title))
				}

				switch my.getType(param.rtParam) {
				case typeStr:
					toolOpts = append(toolOpts, mcp.WithString(pName, propertyOpts...))
				case typeInt:
					toolOpts = append(toolOpts, mcp.WithNumber(pName, propertyOpts...))
				case typeNumber:
					toolOpts = append(toolOpts, mcp.WithNumber(pName, propertyOpts...))
				case typeBool:
					toolOpts = append(toolOpts, mcp.WithBoolean(pName, propertyOpts...))
				case typeObj:
					toolOpts = append(toolOpts, mcp.WithObject(pName, propertyOpts...))
				case typeArray:
					toolOpts = append(toolOpts, mcp.WithArray(pName, propertyOpts...))
				}
			}
		}

		mcpTools = append(mcpTools, mcp.NewTool(funcName, toolOpts...))
	}
	return mcpTools
}

func (my *ToolParser) getType(rt reflect.Type) string {
	switch rt.Kind() {
	case reflect.String:
		return typeStr
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
		reflect.Int64, reflect.Uint64, reflect.Float32, reflect.Float64:
		return typeNumber
	case reflect.Bool:
		return typeBool
	case reflect.Array, reflect.Slice:
		return typeArray
	case reflect.Struct, reflect.Map:
		return typeObj
	default:
		panic("不支持的数据类型")
	}
}

func (my *ToolParser) ToolHandlerFunc(_ context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	//defer func() {
	//	recoverErr := recover()
	//	switch e := recoverErr.(type) {
	//	case string:
	//		err = errors.New(e)
	//	case error:
	//		err = e
	//	default:
	//		err = errors.New("未知错误，请稍后重试")
	//	}
	//}()

	params := request.Params
	functionName := params.Name
	arguments := params.Arguments
	funcMapping := my.funcName2Mapping[functionName]
	if funcMapping == nil {
		err := errors.New(fmt.Sprintf("没有找到名为[%s]的工具", functionName))
		return nil, err
	}

	funcParams := funcMapping.funcParams
	var results []reflect.Value
	if len(funcParams) <= 0 {
		results = funcMapping.rvFunc.Call(nil)
	} else if len(funcParams) == 1 && funcParams[0].rtParam.Kind() == reflect.Struct {
		rtParam := funcParams[0].rtParam
		p := reflect.New(rtParam)
		jsonBytes, err := json.Marshal(arguments)
		if err != nil {
			return nil, err
		}
		if err = json.Unmarshal(jsonBytes, p.Interface()); err != nil {
			return nil, err
		}
		if !funcParams[0].definedParamIsPtr {
			p = p.Elem()
		}
		results = funcMapping.rvFunc.Call([]reflect.Value{p})
	} else {
		rvParams, err := my.prepareParams(arguments, funcParams)
		if err != nil {
			return nil, err
		}
		results = funcMapping.rvFunc.Call(rvParams)
	}

	var val any
	for _, rvVal := range results {
		v := rvVal.Interface()
		if e, ok := v.(error); ok && e != nil {
			return nil, e
		}
		val = v
	}

	if s, ok := val.(string); ok {
		return mcp.NewToolResultText(s), nil
	} else if callToolResult, ok := val.(*mcp.CallToolResult); ok {
		return callToolResult, nil
	}
	s, err := toolkit.ConvertStr4Any(val)
	if err != nil {
		bs, err2 := json.Marshal(val)
		if err2 != nil {
			return nil, err2
		}
		s = string(bs)
	}
	return mcp.NewToolResultText(s), nil
}

func (my *ToolParser) prepareParams(paramsMap map[string]any, funcParams []FuncParamDesc) ([]reflect.Value, error) {
	// 构造方法入参列表
	rvParams := make([]reflect.Value, 0)
	for _, funParam := range funcParams {
		paramName := funParam.pName
		if strutil.IsBlank(paramName) {
			return nil, errors.New("绑定参数失败：参数名为空")
		}
		// 创建实例
		var rvParamPtr reflect.Value
		rtParam := funParam.rtParam

		pVal := paramsMap[paramName]
		switch rtParam.Kind() {
		case reflect.Slice:
			if pVal == nil {
				rvParamPtr = reflect.New(rtParam)
				break
			}
			pKind := tools.IndirectReflectType(pVal).Kind()
			if pKind != reflect.Slice && pKind != reflect.Array {
				return nil, errors.New(fmt.Sprintf("参数名为[%s]的值类型不匹配", paramName))
			}

			rvP := reflect.Indirect(reflect.ValueOf(pVal))
			// 根据 rtParam 创建一个切片
			rvParam := reflect.MakeSlice(rtParam, 0, rvP.Len())
			for i := 0; i < rvP.Len(); i++ {
				ele := rvP.Index(i)
				convVal, err := toolkit.ReflectConvert4Any(rtParam.Elem(), ele)
				if err != nil {
					return nil, err
				}
				rvParam = reflect.Append(rvParam, convVal.Elem())
			}
			// 统一转为指针
			rvParamPtr = reflect.New(rtParam)
			rvParamPtr.Elem().Set(rvParam)
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
			reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
			// 统一转化为指针
			if pVal == nil {
				if funParam.definedParamIsPtr {
					rvParamPtr = reflect.Zero(reflect.New(rtParam).Type())
				} else {
					rvParamPtr = reflect.New(rtParam)
					rvParamPtr.Elem().Set(reflect.Zero(rtParam))
				}
			} else {
				rv, err := toolkit.ReflectConvert4Any(rtParam, pVal)
				if err != nil {
					return nil, err
				}
				rvParamPtr = rv
			}
		default:
			msg := fmt.Sprintf("不支持%s类型参数的解析", rtParam.Kind())
			panic(msg)
		}
		// 方法入参填值
		if funParam.definedParamIsPtr {
			rvParams = append(rvParams, rvParamPtr)
		} else {
			rvParams = append(rvParams, rvParamPtr.Elem())
		}
	}
	return rvParams, nil
}

// ParseTools 解析 Tool
func (my *ToolParser) ParseTools(tools ...ToolsAnnotation) {
	if len(tools) <= 0 {
		return
	}

	for _, toolObj := range tools {
		my.doParseSingle(toolObj)
	}

	my.mcpTools = my.buildMcpTools()
}

func (my *ToolParser) doParseSingle(tool ToolsAnnotation) {
	my.mustPoint(tool)
	var mappings []*FuncMapping
	rvToolObj := reflect.Indirect(reflect.ValueOf(tool))
	rtToolObj := rvToolObj.Type()
	if rtToolObj.Kind() != reflect.Struct {
		return
	}

	for i := 0; i < rtToolObj.NumField(); i++ {
		structField := rtToolObj.Field(i)
		if structField.Anonymous {
			// 内嵌字段，即继承字段，忽略
			continue
		}

		fieldTag := structField.Tag

		rtField := structField.Type
		if rtField.Kind() == reflect.Ptr {
			rtField = rtField.Elem()
		}
		if rtField == mcpToolRt {
			toolObjFullName := tools.GetFullPathName(rtToolObj)
			mapping := new(FuncMapping)

			funcName := tools.FirstLetter2Upper(structField.Name)
			mapping.name = funcName

			my.parseTag(fieldTag, mapping, toolObjFullName, funcName)

			rvFun := my.findToolMethod(rvToolObj, funcName)
			mapping.rvFunc = rvFun

			parameters := my.parseParameters(funcName, rvFun, my.parseFunParamNames(fieldTag, funcName))
			mapping.funcParams = parameters

			mappings = append(mappings, mapping)
		}
	}

	for _, funcMapping := range mappings {
		if funcMapping != nil {
			my.funcName2Mapping[funcMapping.name] = funcMapping
		}
	}
}

func (my *ToolParser) parseTag(fieldTag reflect.StructTag, mapping *FuncMapping, toolObjFullName, funcName string) {
	if funcDesc, ok := fieldTag.Lookup(toolDescTag); ok {
		mapping.description = funcDesc
	} else {
		msg := fmt.Sprintf("McpTools[%s]中方法[%s]缺少tool描述tag[%s]", toolObjFullName, funcName, toolDescTag)
		panic(errors.New(msg))
	}
}

func (my *ToolParser) findToolMethod(rvToolObj reflect.Value, funcName string) reflect.Value {
	var rvFun reflect.Value
	rtToolObj := rvToolObj.Type()
	toolObjFullName := tools.GetFullPathName(rtToolObj)
	if fun, ok := rtToolObj.MethodByName(funcName); ok {
		if strutil.IsNotBlank(fun.PkgPath) {
			msg := fmt.Sprintf("AiTools[%s]中方法[%s]不可导出", toolObjFullName, funcName)
			panic(msg)
		}
		rvFun = rvToolObj.MethodByName(funcName)
	} else {
		rvToolObjPtr := rvToolObj.Addr()
		fun, ok = rvToolObjPtr.Type().MethodByName(funcName)
		if !ok {
			msg := fmt.Sprintf("AiTools[%s]中找不到方法[%s]", toolObjFullName, funcName)
			panic(msg)
		}
		if strutil.IsNotBlank(fun.PkgPath) {
			msg := fmt.Sprintf("AiTools[%s]中方法[%s]不可导出", toolObjFullName, funcName)
			panic(msg)
		}
		rvFun = rvToolObjPtr.MethodByName(funcName)
	}
	return rvFun
}

type funcParamAndDescObj struct {
	name       string
	desc       string
	required   bool
	defaultVal string
	enum       []string
	title      string
}

func (my *ToolParser) parseFunParamNames(fieldTag reflect.StructTag, funcName string) []funcParamAndDescObj {
	// 解析请求参数名称和描述[requestParamsTag]，
	// 格式：jsonschema:"name=参数名称,description=参数描述,required,default=hzm,enum=1&2&3,title=wyy;name=参数名称,description=参数描述"
	// 当方法参数为若干个基础类型的参数时，需要使用此tag声明
	var paramTags []funcParamAndDescObj
	if pNameAndDescStr, ok := fieldTag.Lookup(jsonschemaTag); ok {
		pNameAndDescList := strings.Split(pNameAndDescStr, ";") // name=参数名称,description=参数描述,required
		for i, pNameAndDesc := range pNameAndDescList {
			pNameAndDesc = strings.Trim(pNameAndDesc, " ")
			key2ValMap := make(map[string]string, 3)
			for _, key2Val := range strings.Split(pNameAndDesc, ",") { // name=参数名称
				key2Val = strings.Trim(key2Val, " ")

				var key string
				var val string
				for j, s := range strings.Split(key2Val, "=") {
					s = strings.Trim(s, " ")
					if j == 0 {
						key = s
					} else if j == 1 {
						val = s
					}
				}
				key2ValMap[key] = val
			}

			var requiredVal bool
			if requiredStr, exist := key2ValMap[required]; exist {
				if requiredStr == "" {
					requiredVal = true
				} else {
					requiredVal, _ = strconv.ParseBool(requiredStr)
				}
			}

			var enumVal []string
			if enumStr, exist := key2ValMap[enum]; exist {
				for _, s := range strings.Split(enumStr, "&") {
					s = strings.Trim(s, " ")
					if s != "" {
						enumVal = append(enumVal, s)
					}
				}
				//enumVal = slices.DeleteFunc(strings.Split(enumStr, "&"), func(s string) bool {
				//	return s == ""
				//})
			}

			funcParamTag := &funcParamAndDescObj{
				name:       key2ValMap[name],
				desc:       key2ValMap[description],
				required:   requiredVal,
				defaultVal: key2ValMap[defaultValTag],
				enum:       enumVal,
				title:      key2ValMap[title],
			}

			if funcParamTag.name == "" {
				panic(errors.New(fmt.Sprintf("方法[%s]的第[%d]个参数名称未定义", funcName, i+1)))
			}
			if funcParamTag.desc == "" {
				panic(errors.New(fmt.Sprintf("参数[%s]描述未定义", funcParamTag.name)))
			}
			paramTags = append(paramTags, *funcParamTag)
		}
	}
	return paramTags
}

// 解析方法结构体类型入参的tag
func (my *ToolParser) resolveFunInParamsTag(rtStruct reflect.Type, fieldName string, defaultValMap map[string]string) {
	// 解析默认值tag（default）
	for i := 0; i < rtStruct.NumField(); i++ {
		field := rtStruct.Field(i)
		if strutil.IsNotBlank(field.PkgPath) {
			// 字段不可导出
			continue
		}
		var key string
		if strutil.IsNotBlank(fieldName) {
			key = fieldName + strutil.Dot + field.Name
		} else {
			key = field.Name
		}

		rtField := field.Type
		if rtField.Kind() == reflect.Ptr {
			rtField = rtField.Elem()
		}
		if rtField.Kind() == reflect.Struct {
			my.resolveFunInParamsTag(rtField, field.Name, defaultValMap)
		}
		if defaultVal, ok := field.Tag.Lookup(defaultValTag); ok {
			defaultValMap[key] = defaultVal
		}
	}
}

func (my *ToolParser) parseParameters(funName string, rvFun reflect.Value, pNameAndDescList []funcParamAndDescObj) []FuncParamDesc {
	rtFun := rvFun.Type()
	outNum := rtFun.NumOut()
	if outNum > 2 {
		msg := fmt.Sprintf("方法[%s]的返回参数个数不能大于2", funName)
		panic(errors.New(msg))
	}
	if outNum == 2 {
		firstRes := rtFun.Out(0)
		secondRes := rtFun.Out(1)
		firstIsErrType := firstRes.Kind() == reflect.Interface && firstRes == reflect.TypeOf((*error)(nil)).Elem()
		secondIsErrType := secondRes.Kind() == reflect.Interface && secondRes == reflect.TypeOf((*error)(nil)).Elem()
		if !firstIsErrType && !secondIsErrType {
			msg := fmt.Sprintf("方法[%s]的返回参数个数为2，必须要声明其中一个参数类型为error类型", funName)
			panic(errors.New(msg))
		}
	}

	inNum := rtFun.NumIn()
	if len(pNameAndDescList) > inNum {
		msg := fmt.Sprintf("方法[%s]的参数名个数大于参数列表个数", funName)
		panic(errors.New(msg))
	}

	// 利用管道FIFO（先进先出）的特性，将参数名称放入管道，方便后面获取
	pNameObjChan := make(chan funcParamAndDescObj, 1)
	go putParamName2Chan(pNameObjChan, pNameAndDescList)

	funParams := make([]FuncParamDesc, 0)
	for i := 0; i < inNum; i++ {
		var funParam FuncParamDesc
		rtParam := rtFun.In(i)
		// 该方法定义参数是否为指针类型
		definedParamIsPtr := rtParam.Kind() == reflect.Ptr
		if definedParamIsPtr {
			rtParam = rtParam.Elem()
		}
		funParam.definedParamIsPtr = definedParamIsPtr

		// 类型分别判断
		switch rtParam.Kind() {
		case reflect.Struct:
			if inNum != 1 {
				panic(errors.New(fmt.Sprintf("方法[%s]结构体类型参数只支持一个入参", funName)))
			}
			key := rtParam.Name()
			schema := jsonschema.Reflect(reflect.New(rtParam).Interface())
			schema = schema.Definitions[key]

			// 如果参数为结构体，解析默认值tag，获取所有字段默认值
			defaultValMap := make(map[string]string)
			my.resolveFunInParamsTag(rtParam, strutil.Empty, defaultValMap)
			funParam.defaultValMap = defaultValMap
			funParam.schema = schema
		case reflect.Slice:
			if definedParamIsPtr {
				msg := fmt.Sprintf("方法[%s]slice类型的入参不能声明为指针", funName)
				panic(errors.New(msg))
			}
			if rtParam.Elem().Kind() == reflect.Struct ||
				rtParam.Elem().Kind() == reflect.Map ||
				rtParam.Elem().Kind() == reflect.Slice ||
				rtParam.Elem().Kind() == reflect.Array ||
				rtParam.Elem().Kind() == reflect.Chan ||
				rtParam.Elem().Kind() == reflect.Func ||
				rtParam.Elem().Kind() == reflect.Interface ||
				rtParam.Elem().Kind() == reflect.UnsafePointer ||
				rtParam.Elem().Kind() == reflect.Ptr {
				panic(errors.New("切片元素数据类型请定义为基本数据类型"))
			}
			paramObj := <-pNameObjChan
			funParam.pName = paramObj.name
			funParam.desc = paramObj.desc
			funParam.required = paramObj.required
			funParam.defaultVal = paramObj.defaultVal
			funParam.enum = paramObj.enum
			funParam.title = paramObj.title
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
			reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
			paramObj := <-pNameObjChan
			funParam.pName = paramObj.name
			funParam.desc = paramObj.desc
			funParam.required = paramObj.required
			funParam.defaultVal = paramObj.defaultVal
			funParam.enum = paramObj.enum
			funParam.title = paramObj.title
		default:
			msg := fmt.Sprintf("方法[%s]入参无法解析，建议定义为基本类型或者结构体", funName)
			panic(msg)
		}
		funParam.rtParam = rtParam
		funParams = append(funParams, funParam)
	}
	return funParams
}

func (my *ToolParser) mustPoint(a any) {
	rt := reflect.TypeOf(a)
	if rt.Kind() != reflect.Ptr {
		msg := fmt.Sprintf("[%s]不是指针类型", rt.Name())
		panic(msg)
	}
}

func putParamName2Chan(pNameObjChan chan<- funcParamAndDescObj, funcParamAndDescObjs []funcParamAndDescObj) {
	defer close(pNameObjChan)
	for _, obj := range funcParamAndDescObjs {
		pNameObjChan <- obj
	}
}
